'use strict';


const axios = require('axios');
const qs = require('qs');
const crypto = require('crypto');
const https = require('https');
const md5 = require('md5');
const { raw, parseXML, buildXML, getNonceStr } = require('./util');
const util = require('./util');
class WechatSDK {
  constructor(opts) {
    this.appId = opts.appId;
    this.appSecret = opts.appSecret;
    this.notifyUrl = opts.notifyUrl;
    this.mchId = opts.mchId;
    this.payKey = opts.payKey;
    this.pfx = opts.pfx;
    this.encodingAESKey = opts.encodingAESKey;
    this.redirectUrl = opts.redirectUrl;
    this.token = opts.token;
  }
  // 微信公众消息推送 获取签名
  getSignature() {
    const raw_signature = [ this.token, ...arguments ].sort().join('');
    const sha1 = crypto.createHash('sha1');
    sha1.update(raw_signature);
    return sha1.digest('hex');
  }
  // 微信公众消息推送 验证签名
  vaildSignature(timestamp, nonce, sign) {
    const verifySign = this.getSignature(timestamp, nonce);
    return verifySign === sign;
  }
  // 加密微信消息
  encryptWechatNoMsg(json) {
    const aesKey = new Buffer(this.encodingAESKey + '=', 'base64');
    const iv = aesKey.slice(0, 16);
    const opts = {
      rootName: 'xml',
      cdata: true,
      headless: true,
      renderOpts: { indent: ' ', pretty: 'true' },
    };
    const xml = buildXML(json, opts);
    const encrypt = util.encryptForAES256CBC(xml, aesKey, iv, this.appId);
    const result = {};
    result.Encrypt = encrypt;
    result.Nonce = parseInt((Math.random() * 100000000000), 10);
    result.TimeStamp = new Date().getTime();
    result.MsgSignature = this.getSignature(result.TimeStamp, result.Nonce, result.Encrypt);
    const data = buildXML(result, opts);
    return data;
  }
  // 解密微信公众号消息推送的消息
  async decryptWechatNoMsg(encrypt) {
    const aesKey = new Buffer(this.encodingAESKey + '=', 'base64');
    const iv = aesKey.slice(0, 16);
    const decryptedMsg = util.decryptForAES256CBC(encrypt, aesKey, iv);
    return await parseXML(decryptedMsg);
  }

  // 解析微信退款回调 的加密字符串
  async decryptReqInfo(req_info) {
    const key = md5(this.payKey);
    const iv = '';
    const xml = util.decryptForAES256ECB(req_info, key, iv);
    return await parseXML(xml);
  }

  // 微信退款
  async refund({
    out_trade_no,
    total_fee,
    refund_fee,
    out_refund_no,
  }, notify_url = this.notifyUrl) {
    const api_url = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
    const params = {
      appid: this.appId,
      mch_id: this.mchId,
      nonce_str: getNonceStr(),
      notify_url,
      out_refund_no,
      total_fee,
      refund_fee,
      out_trade_no,
    };
    const sign = this.getPaySign(params);
    const data = Object.assign(params, { sign });
    const result = await axios({
      method: 'POST',
      url: api_url,
      passphrase: this.mchId,
      data: buildXML(data),
      httpsAgent: new https.Agent({
        pfx: this.pfx,
        passphrase: this.mchId,
      }),
    });
    return await parseXML(result.data);
  }
  /**
   * 获取微信 SessionKey
   * @param {String} code  微信小程序获取的token 返回的CODE
   * @return {Object}  微信返回的饿结果
   */
  async getSessionKeyByCode(code) {
    const params = {
      js_code: code,
      appid: this.appId,
      secret: this.appSecret,
      grant_type: 'authorization_code',
    };
    const result = await axios(
      `https://api.weixin.qq.com/sns/jscode2session?${qs.stringify(params)}`
    );
    return result.data;
  }
  /**
   * 解析微信用户信息
   * @param {String} sessionKey  微信小程序获取的token 返回的CODE
   * @param {String} encryptedData  微信小程序获取的token 返回的CODE
   * @param {String} iv  微信小程序获取的token 返回的CODE
   * @return {Object}  微信返回的饿结果
   */

  getUserInfoForWechatApp(sessionKey, encryptedData, iv) {
    return util.decryptForAES128CBC(sessionKey, encryptedData, iv);
  }

  async getWechatAppUserInfo(code, encryptedData, iv) {
    const data = await this.getSessionKeyByCode(code);
    if (!data.session_key) return null;
    const userInfo = this.getUserInfoForWechatApp(data.session_key, encryptedData, iv);
    return userInfo;
  }
  // 微信统一下单Api
  async unifiedOrder({
    openid,
    body,
    ip,
    amount,
    out_trade_no,
  }, notifyUrl = this.notifyUrl) {
    const params = {
      appid: this.appId,
      body,
      device_info: 'WEB',
      mch_id: this.mchId,
      nonce_str: getNonceStr(),
      notify_url: notifyUrl,
      openid,
      out_trade_no,
      fee_type: 'CNY',
      sign_type: 'MD5',
      spbill_create_ip: ip,
      total_fee: Number(amount),
      trade_type: 'JSAPI',
    };
    const sign = this.getPaySign(params);
    const data = Object.assign(params, { sign });
    const result = await axios({
      method: 'post',
      url: 'https://api.mch.weixin.qq.com/pay/unifiedorder',
      data: buildXML(data),
    });
    return await parseXML(result.data);
  }
  // 获取支付签名
  getPaySign(data) {
    const string = `${raw(data)}&key=${this.payKey}`;
    const hash = crypto.createHash('MD5');
    hash.update(string);
    const sign = hash.digest('hex');
    return sign.toLocaleUpperCase();
  }
  // 验证支付签名
  verifyPaySign(payload) {
    const sign = payload.sign || '';
    delete payload.sign;
    const paySign = this.getPaySign(payload);
    return paySign === sign;
  }
  async getCodeAccessToken(code) {
    const params = {
      code,
      appid: this.appId,
      secret: this.appSecret,
      grant_type: 'authorization_code',
    };
    const result = await axios(`https://api.weixin.qq.com/sns/oauth2/access_token?${qs.stringify(params)}`);
    return JSON.parse(result.data.toString());
  }
  //  通过accessToken 获取用户信息
  async getUserInfoByAccessToken(openid, access_token) {
    const params = {
      access_token,
      openid,
      lang: 'zh_CN',
    };
    const result = await axios(`https://api.weixin.qq.com/sns/userinfo?${qs.stringify(params)}`);
    return JSON.parse(result.data.toString());
  }
  // 获取accessToken
  async getAccessToken() {
    const params = {
      appid: this.appId,
      secret: this.appSecret,
      grant_type: 'client_credential',
    };
    const { data } = await axios(`https://api.weixin.qq.com/cgi-bin/token?${qs.stringify(params)}`);
    return data.access_token;
  }

}
module.exports = WechatSDK;
